package com.atguigu.gmall.search.service.impl;

import com.atguigu.gmall.search.Goods;
import com.atguigu.gmall.search.repo.GoodsRepositories;
import com.atguigu.gmall.search.service.SearchService;
import com.atguigu.gmall.search.vo.SearchParamVo;
import com.atguigu.gmall.search.vo.SearchRespVo;
import com.atguigu.gmall.search.vo.SearchRespVo.OrderMap;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author lhh
 * @ClassName GoodsServiceImpl
 * @date 2022 2022/12/12
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Resource
    GoodsRepositories goodsRepositories;

    @Resource
    ElasticsearchRestTemplate restTemplate;

    int pageSize=10;

    @Override
    public void down(Long skuId) {
        goodsRepositories.deleteById(skuId);
    }

    @Override
    public void up(Goods goods) {
        goodsRepositories.save(goods);
    }

    @Override
    public SearchRespVo search(SearchParamVo searchParamVo) {

        //根据前端请求参数，构建查询条件
        Query query = getQuery(searchParamVo);
        //检索结果
        SearchHits<Goods> goods = restTemplate.search(query,
                Goods.class,
                IndexCoordinates.of("goods"));
        //解析得到的数据并封装返回前端
        SearchRespVo respVo=builderSearchResp(goods,searchParamVo);
        return respVo;
    }

    @Override
    public void updateHotScore(Long skuId, Long score) {
        Document document = Document.create();
        document.put("hotScore",score);
        UpdateQuery query = UpdateQuery.builder(skuId + "")
                .withDocAsUpsert(true)
                .withDocument(document).build();
        restTemplate.update(query,IndexCoordinates.of("goods"));
    }

    private SearchRespVo builderSearchResp(SearchHits<Goods> goods,SearchParamVo searchParamVo) {

        SearchRespVo respVo = new SearchRespVo();

        //检索参数
        respVo.setSearchParamVo(searchParamVo);

        //品牌面包屑
        if (!StringUtils.isEmpty(searchParamVo.getTrademark())){
            respVo.setTrademarkParam("品牌: "+searchParamVo.getTrademark().split(":")[1]);
        }

        //属性面包屑
        if (searchParamVo.getProps()!=null && searchParamVo.getProps().length>0) {
            List<SearchRespVo.Props> propsList = Arrays.stream(searchParamVo.getProps())
                    .map(item -> {
                        String[] split = item.split(":");
                        SearchRespVo.Props props = new SearchRespVo.Props();
                        props.setAttrName(split[2]);
                        props.setAttrValue(split[1]);
                        props.setAttrId(Long.parseLong(split[0]));
                        return props;
                    }).collect(Collectors.toList());
            respVo.setPropsParamList(propsList);
        }

        //品牌列表 聚合 aggregations
        ParsedLongTerms tmIdAgg = goods.getAggregations().get("tmIdAgg");
        List<SearchRespVo.Trademark> trademarks = tmIdAgg.getBuckets().stream()
                .map(bucket -> {
                    SearchRespVo.Trademark trademark = new SearchRespVo.Trademark();
                    //tmid
                    trademark.setTmId(bucket.getKeyAsNumber().longValue());
                    ParsedStringTerms tmNameAgg = bucket.getAggregations().get("tmNameAgg");
                    String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();
                    //tmName
                    trademark.setTmName(tmName);
                    ParsedStringTerms tmLogoAgg = bucket.getAggregations().get("tmLogoAgg");
                    String tmLogoUrl = tmLogoAgg.getBuckets().get(0).getKeyAsString();
                    //tmLogoUrl
                    trademark.setTmLogoUrl(tmLogoUrl);
                    return trademark;
                }).collect(Collectors.toList());
        respVo.setTrademarkList(trademarks);
        //属性列表 聚合 aggregations

        ParsedNested attrsAgg = goods.getAggregations().get("attrsAgg");
        ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attrIdAgg");

        List<SearchRespVo.Arrts> arrtsList = attrIdAgg.getBuckets().stream().map(bucket -> {
            SearchRespVo.Arrts arrts = new SearchRespVo.Arrts();
            //attrId
            long attrId = bucket.getKeyAsNumber().longValue();
            arrts.setAttrId(attrId);
            //attrName
            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
            arrts.setAttrName(attrName);
            //AttrValueList
            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            List<String> stringList = attrValueAgg.getBuckets().stream()
                    .map(item -> item.getKeyAsString()).collect(Collectors.toList());
            arrts.setAttrValueList(stringList);
            return arrts;
        }).collect(Collectors.toList());
        respVo.setAttrsList(arrtsList);

        //url参数
        String urlParam = builderUrlParam(searchParamVo);
        respVo.setUrlParam(urlParam);
        //排序信息
        if (!StringUtils.isEmpty(searchParamVo.getOrder())){
            OrderMap orderMap = new OrderMap();
            String[] split = searchParamVo.getOrder().split(":");
            orderMap.setSort(split[1]);
            orderMap.setType(split[0]);
            respVo.setOrderMap(orderMap);
        }

        //商品列表
        List<Goods> goodsList = goods.getSearchHits().stream()
                                .map(good -> {
                                    Goods content = good.getContent();
                                    if(!StringUtils.isEmpty(searchParamVo.getKeyword())){
                                        String newTitle = good.getHighlightField("title").get(0);
                                        content.setTitle(newTitle);
                                    }
                                   return content;
                                })
                                .collect(Collectors.toList());
        respVo.setGoodsList(goodsList);
        //页码
        respVo.setPageNo(searchParamVo.getPageNo());

        //总记录数
        //总页码
        long totalHits = goods.getTotalHits();
        respVo.setTotalPages(totalHits % pageSize == 0 ? totalHits / pageSize : totalHits / pageSize + 1);
        return respVo;
    }

    private String builderUrlParam(SearchParamVo searchParamVo) {
        StringBuilder builder=new StringBuilder("list.html?");
        if(searchParamVo.getCategory1Id()!=null){
            builder.append("&category1Id="+searchParamVo.getCategory1Id());
        }
        if(searchParamVo.getCategory2Id()!=null){
            builder.append("&category2Id="+searchParamVo.getCategory2Id());
        }
        if(searchParamVo.getCategory3Id()!=null){
            builder.append("&category3Id="+searchParamVo.getCategory3Id());
        }

        if (!StringUtils.isEmpty(searchParamVo.getKeyword())){
            builder.append("&keyword="+searchParamVo.getKeyword());
        }
        if (!StringUtils.isEmpty(searchParamVo.getTrademark())){
            builder.append("&trademark="+searchParamVo.getTrademark());
        }
        //属性
        if(searchParamVo.getProps()!=null && searchParamVo.getProps().length>0){
            Arrays.stream(searchParamVo.getProps()).forEach(item->{
                builder.append("&props="+item);
            });
        }
        return builder.toString();
    }

    private Query getQuery(SearchParamVo searchParamVo) {
        //查询条件
        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        //1.1、一级分类
        if (searchParamVo.getCategory1Id() != null) {
            bool.must(QueryBuilders.termQuery("category1Id", searchParamVo.getCategory1Id()));
        }
        //1.2、二级分类
        if (searchParamVo.getCategory2Id() != null) {
            bool.must(QueryBuilders.termQuery("category2Id", searchParamVo.getCategory2Id()));
        }
        //1.3、三级分类
        if (searchParamVo.getCategory3Id() != null) {
            bool.must(QueryBuilders.termQuery("category3Id", searchParamVo.getCategory3Id()));
        }
        //1.4、关键字
        if (!StringUtils.isEmpty(searchParamVo.getKeyword())) {
            bool.must(QueryBuilders.matchQuery("title", searchParamVo.getKeyword()));
        }

        //1.5、品牌查询
        if (!StringUtils.isEmpty(searchParamVo.getTrademark())) {
            String[] split = searchParamVo.getTrademark().split(":");
            bool.must(QueryBuilders.termQuery("tmId", split[0]));
        }

        //1.6、属性检索
        if (searchParamVo.getProps() != null && searchParamVo.getProps().length > 0) {
            Arrays.stream(searchParamVo.getProps())
                    .forEach(item->{
                        String[] split = item.split(":");
                        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

                        //属性id
                        boolQuery.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
                        //属性值条件
                        boolQuery.must(QueryBuilders.termQuery("attrs.attrValue",split[1]));
                        NestedQueryBuilder nested = QueryBuilders.nestedQuery("attrs",boolQuery, ScoreMode.None);
                        bool.must(nested);
                    });
        }
        NativeSearchQuery query = new NativeSearchQuery(bool);
        //排序方式
        if (!StringUtils.isEmpty(searchParamVo.getOrder())) {
            Sort sort = null;

            String[] split = searchParamVo.getOrder().split(":");
            Sort.Direction direction= "asc".equalsIgnoreCase(split[1])?Sort.Direction.ASC:Sort.Direction.DESC;
            switch (split[0]){
                case "1":
                    sort=Sort.by(direction, "hotScore");
                    break;
                case "2":
                    sort=Sort.by(direction, "price");
                    break;
                default:
                    sort=Sort.by(Sort.Direction.DESC, "hotScore");
            }
            query.addSort(sort);
        }

        //分页
        Pageable pageable=PageRequest.of(searchParamVo.getPageNo()-1,pageSize);
        query.setPageable(pageable);

        //品牌id聚合
        TermsAggregationBuilder tmIdAgg = AggregationBuilders.terms("tmIdAgg")
                .field("tmId")
                .size(200);
        //name子聚合
        tmIdAgg.subAggregation(AggregationBuilders.terms("tmNameAgg")
                .field("tmName")
                .size(1));
        //value自聚合
        tmIdAgg.subAggregation(AggregationBuilders.terms("tmLogoAgg")
                .field("tmLogoUrl")
                .size(1));
        query.addAggregation(tmIdAgg);

        //品牌属性值聚合
        NestedAggregationBuilder attrsAgg = AggregationBuilders.nested("attrsAgg", "attrs");
        //attrId聚合
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg")
                .field("attrs.attrId")
                .size(200);
        //name聚合
        attrIdAgg.subAggregation(AggregationBuilders.terms("attrNameAgg")
                .field("attrs.attrName")
                .size(1));
        //value聚合
        attrIdAgg.subAggregation(AggregationBuilders.terms("attrValueAgg")
                .field("attrs.attrValue")
                .size(200));
        attrsAgg.subAggregation(attrIdAgg);
        query.addAggregation(attrsAgg);

        //高亮
        if(!StringUtils.isEmpty(searchParamVo.getKeyword())){
            HighlightBuilder builder = new HighlightBuilder();
            builder.field("title")
                    .preTags("<span style='color:red'>")
                    .postTags("</span>");
            HighlightQuery highlightQuery = new HighlightQuery(builder);
            query.setHighlightQuery(highlightQuery);
        }
        return query;
    }
}
